package com.ewili.framework.utils;

import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;

/**
 * 生成消息摘要工具类
 *
 * @author leo
 */
public final class DigestUtils {

    private DigestUtils() {
    }

    /**
     * 对输入字符串进行md5散列.
     *
     * @param input
     * @return
     */
    public static String md5(String input, Charset charset, Encoding encoding) {
        return digest(input, null, Algorithm.MD5, charset, 1, encoding);
    }

    /**
     * 对输入字符串进行md5散列.
     *
     * @param input
     * @param salt
     * @return
     */
    public static String md5(String input, String salt, Charset charset, Encoding encoding) {
        return digest(input, salt, Algorithm.MD5, charset, 1, encoding);
    }

    /**
     * 对输入字符串进行sha1散列.
     *
     * @param input
     * @return
     */
    public static String sha1(String input, Charset charset, Encoding encoding) {
        return digest(input, null, Algorithm.SHA1, charset, 1, encoding);
    }

    /**
     * 对输入字符串进行sha1散列.
     *
     * @param input
     * @param salt
     * @return
     */
    public static String sha1(String input, String salt, Charset charset, Encoding encoding) {
        return digest(input, salt, Algorithm.SHA1, charset, 1, encoding);
    }

    /**
     * 使用utf-8解码字符串，并执行散列
     *
     * @param input
     * @param algorithm
     * @param salt
     * @param iterations
     * @return
     */
    private static String digest(String input, String salt,
                                 Algorithm algorithm, Charset charset, int iterations,
                                 Encoding encoding) {
        byte[] bytes = digest(StringUtils.getBytes(input, charset),
                StringUtils.getBytes(salt, charset), algorithm, iterations);

        return encoding.encode(bytes);
    }

    /**
     * 支持md5与sha1散列算法.
     *
     * @param input
     * @param algorithm
     * @param salt
     * @param iterations
     * @return
     */
    private static byte[] digest(byte[] input, byte[] salt, Algorithm algorithm, int iterations) {
        try {
            MessageDigest digest = MessageDigest.getInstance(algorithm.getAlgorithm());

            if (salt != null) {
                digest.update(salt);
            }

            byte[] result = digest.digest(input);

            for (int i = 1; i < iterations; i++) {
                digest.reset();
                result = digest.digest(result);
            }
            return result;
        } catch (GeneralSecurityException e) {
            throw ExceptionUtils.propagate(e);
        }
    }

    /**
     * 支持的散列算法
     *
     * @author leo
     */
    public enum Algorithm {

        MD5("MD5"),
        SHA1("SHA-1");

        private final String algorithm;

        Algorithm(String algorithm) {
            this.algorithm = algorithm;
        }

        public String getAlgorithm() {
            return this.algorithm;
        }
    }

    /**
     * 支持的输出字符串编码方式
     *
     * @author leo
     */
    public enum Encoding {

        BASE64,
        HEX;

        public String encode(byte[] bytes) {
            if (this == BASE64) {
                return StringUtils.encodeBase64(bytes);
            } else if (this == HEX) {
                return StringUtils.encodeHex(bytes);
            } else {
                throw new UnsupportedOperationException();
            }
        }
    }

}